﻿/*****************************************************************************
 * Hyldahl.Services.TaskProcessing                 Created by: MSH 2008.07.22
 * --------------------------------------------------------------------------
 * FileName: Workers\Worker.cs
 * --------------------------------------------------------------------------
 * Copyright 2009 Martin Sixhøj Hyldahl
 *****************************************************************************/

using System;
using System.Threading;
using System.Diagnostics;

namespace Hyldahl.Services.TaskProcessing.Workers
{
    public abstract class Worker
    {
        /*****************************************************
         * FIELDS
         *****************************************************/
        private TaskProcessingController controller;
        private WorkerState             state           = WorkerState.Stopped;
        private Thread                  workerThread;
        private string                  name;
        private TraceSource             traceSource;
        private bool                    stopFlag;

        /*****************************************************
         * CONSTRUCTOR
         *****************************************************/

        /// <summary>
        /// Initializes a new instance of the <see cref="Worker"/> class.
        /// </summary>
        /// <param name="controller">The controller.</param>
        /// <param name="name">The name.</param>
        public Worker(TaskProcessingController controller, string name)
        {
            this.Controller = controller;
            this.Name = name;

            this.traceSource = new TraceSource(this.GetType().Name); // controller.TraceSource;
        }

        /*****************************************************
         * METHODS
         *****************************************************/

        /// <summary>
        /// Starts this instance.
        /// </summary>
        public void Start()
        {
            //traceSource.TraceInformation("Starting thread");

            this.StopFlag = false;

            workerThread = new Thread(new ThreadStart(WorkerThreadStart));
            workerThread.Start();

            this.State = WorkerState.Started;
        }

        /// <summary>
        /// Stops this instance.
        /// </summary>
        public void Stop()
        {
            traceSource.TraceInformation("Stopping thread");

            if ((this.State & WorkerState.Started)  == WorkerState.Started ||
                (this.State & WorkerState.Paused)   == WorkerState.Paused)
            {
                this.StopFlag = true;

                //if(workerThread.ThreadState == System.Threading.ThreadState.Running)
                //    workerThread.Join();

                // this.State = WorkerState.Stopped;
            }
        }

        /// <summary>
        /// Kills this instance.
        /// </summary>
        public void Kill()
        {
            traceSource.TraceInformation("Killing thread");

            if ((this.State & WorkerState.Started) == WorkerState.Started ||
                (this.State & WorkerState.Paused) == WorkerState.Paused)
            {
                this.StopFlag = true;

                try
                {
                    // Interrupt sleep
                    workerThread.Interrupt();

                    if (workerThread.ThreadState == System.Threading.ThreadState.Running ||
                        workerThread.ThreadState == System.Threading.ThreadState.WaitSleepJoin)
                        workerThread.Abort();
                }
                catch
                { /* Ignore error caused by thread abort */ }

                this.State = WorkerState.Stopped;
            }
        }

        /// <summary>
        /// Waits for completion.
        /// </summary>
        public void WaitForCompletion()
        {
            WaitForCompletion(TimeSpan.Zero);
        }

        /// <summary>
        /// Waits for completion.
        /// </summary>
        /// <param name="timeout">The timeout.</param>
        /// <returns></returns>
        public bool WaitForCompletion(TimeSpan timeout)
        {
            DateTime startWaitDate = DateTime.Now;

            while ((this.State & WorkerState.Started) == WorkerState.Started)
            {
                if (timeout != TimeSpan.Zero)
                {
                    if (startWaitDate.Add(timeout) > DateTime.Now)
                        break;
                }

                Thread.Sleep(200);
            }

            return (this.State & WorkerState.Stopped) == WorkerState.Stopped;
        }

        /*****************************************************
         * PRIVATE METHODS
         *****************************************************/

        /// <summary>
        /// Worker thread.
        /// </summary>
        protected abstract void WorkerThreadStart();

        /*****************************************************
         * PROPERTIES
         *****************************************************/

        /// <summary>
        /// Gets or sets the controller.
        /// </summary>
        /// <value>The controller.</value>
        public TaskProcessingController Controller
        {
            get { return controller; }
            private set
            {
                if (value == null)
                    throw new ArgumentNullException("value");

                controller = value;
            }
        }

        /// <summary>
        /// Gets or sets the state.
        /// </summary>
        /// <value>The state.</value>
        public WorkerState State
        {
            get { return state; }
            protected set { state = value; }
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>The name.</value>
        public string Name
        {
            get { return name; }
            private set { name = value; }
        }

        /// <summary>
        /// Gets the trace source.
        /// </summary>
        /// <value>The trace source.</value>
        public TraceSource TraceSource
        {
            get
            {
                return traceSource;
            }
            protected set
            {
                if (value == null)
                    throw new ArgumentNullException("value");

                traceSource = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [stop flag].
        /// </summary>
        /// <value><c>true</c> if [stop flag]; otherwise, <c>false</c>.</value>
        protected bool StopFlag
        {
            get
            {
                lock (this)
                {
                    return stopFlag;
                }
            }
            set
            {
                lock (this)
                {
                    stopFlag = value;
                }
            }
        }

    }
}